package com.zys.sac.core.config;

import com.zys.sac.core.filter.SacCheckFilter;
import com.zys.sac.core.filter.VerifyCodeFilter;
import com.zys.sac.core.holder.EnhanceFilterHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import javax.servlet.Filter;

/**
 * Created by zhongjunkai on 2022/10/24.
 */
public class SacNewWebSecurityConfiguration {

    @Autowired
    private AuthenticationSuccessHandler authenticationSuccessHandler;

    @Autowired
    private AuthenticationFailureHandler authenticationFailureHandler;

    @Autowired
    private AccessDeniedHandler accessDeniedHandler;

    @Autowired
    private AuthenticationEntryPoint authenticationEntryPoint;

    @Autowired
    private AccessDecisionManager accessDecisionManager;

    @Autowired
    private SacCheckFilter checkFilter;

    @Autowired
    private FilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource;

    @Autowired(required = false)
    private VerifyCodeFilter verifyCodeFilter;

    @Autowired
    private LogoutSuccessHandler logoutSuccessHandler;

    @Autowired(required = false)
    private EnhanceFilterHolder enhanceFilterHolder;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
        loadVerifyCodeFilter(http);
        loadIdentifierCheckFilter(http);
        http.csrf().disable();
        http.formLogin()
                .loginProcessingUrl(loginPath())
                .permitAll()
                .successHandler(authenticationSuccessHandler)
                .failureHandler(authenticationFailureHandler)
                .and()
                .exceptionHandling()
                .accessDeniedHandler(accessDeniedHandler)
                .authenticationEntryPoint(authenticationEntryPoint);
        http.sessionManagement().disable(); // 关闭session管理模式，采用自定义的token机制
        http.authorizeRequests()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                        object.setAccessDecisionManager(accessDecisionManager);
                        object.setSecurityMetadataSource(filterInvocationSecurityMetadataSource);
                        return object;
                    }
                })
                .anyRequest().permitAll()//对于非特殊的所有请求均采用通过的方案
                .and()
                .logout()
                .logoutSuccessHandler(logoutSuccessHandler)
                .logoutUrl(logoutPath())
                .logoutSuccessUrl(logoutSuccUrl())
        ;
        return http.build();
    }


    private String loginPath() {
        return getCheckFilter().loginPath();
    }

    private String logoutPath() {
        return getCheckFilter().logoutPath();
    }

    private String logoutSuccUrl() {
        return getCheckFilter().logoutSuccessUrl();
    }

    protected SacCheckFilter getCheckFilter() {
        return checkFilter;
    }

    /**
     * 加载身份校验过滤器，主要是对当前登录用户的身份进行进行鉴权
     *
     * @param http
     */
    private void loadIdentifierCheckFilter(HttpSecurity http) {
        addFilters(http, "pre");
        //设置Token校验过滤器，使该过滤器在UsernamePasswordAuthenticationFilter过滤器之前执行
        http.addFilterAfter(getCheckFilter(), UsernamePasswordAuthenticationFilter.class);
        addFilters(http, "after");
    }

    private void addFilters(HttpSecurity http, String position) {
        if (enhanceFilterHolder != null) {
            if ("pre".equals(position)) {
                for (Filter preFilter : enhanceFilterHolder.preFilters()) {
                    http.addFilterBefore(preFilter, UsernamePasswordAuthenticationFilter.class);
                }
            } else if ("after".equals(position)) {
                for (Filter afterFilter : enhanceFilterHolder.afterFilters()) {
                    http.addFilterAfter(afterFilter, UsernamePasswordAuthenticationFilter.class);
                }
            }
        }
    }

    /**
     * 加载验证码校验过滤器，因为某些场景不需要验证码校验，所以这部分过滤器按需加载，当spring容器中不存在该过滤器实例bean时，就不加载该过滤器
     *
     * @param http
     */
    private void loadVerifyCodeFilter(HttpSecurity http) {
        //因为某些场景可能不需要验证码的校验，验证码过滤器按需加载
        if(verifyCodeFilter != null) {
            http.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class);
        }
    }
}
